use strict;

my $in = <<IN 
int fz_getattr(const char *path, struct stat *statbuf);
int fz_readlink(const char *path, char *link, size_t size);
int fz_mknod(const char *path, mode_t mode, dev_t dev);
int fz_mkdir(const char *path, mode_t mode);
int fz_unlink(const char *path);
int fz_rmdir(const char *path);
int fz_symlink(const char *path, const char *link);
int fz_rename(const char *path, const char *newpath);
int fz_link(const char *path, const char *newpath);
int fz_chmod(const char *path, mode_t mode);
int fz_chown(const char *path, uid_t uid, gid_t gid);
int fz_truncate(const char *path, off_t newsize);
int fz_utime(const char *path, struct utimbuf *ubuf);
int fz_open(const char *path, struct fuse_file_info *fi);
int fz_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi);
int fz_write(const char *path, const char *buf, size_t size, off_t offset, fuse_file_info *fi);
int fz_statfs(const char *path, struct statvfs *statv);
int fz_flush(const char *path, struct fuse_file_info *fi);
int fz_release(const char *path, struct fuse_file_info *fi);
int fz_fsync(const char *path, int datasync, struct fuse_file_info *fi);
int fz_setxattr(const char *path, const char *name, const char *value, size_t size, int flags);
int fz_getxattr(const char *path, const char *name, char *value, size_t size);
int fz_listxattr(const char *path, char *list, size_t size);
int fz_removexattr(const char *path, const char *name);
int fz_opendir(const char *path, struct fuse_file_info *fi);
int fz_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi);
int fz_releasedir(const char *path, struct fuse_file_info *fi);
int fz_fsyncdir(const char *path, int datasync, struct fuse_file_info *fi);
IN
;
$in =~ s/[\r\n]//gs;

my (@fuse_init_spec, @fuse_private_methods_spec, @fslayer_spec, @fslayer_methods_spec, @fslayer_macross);
foreach(split ';', $in) {
	my ($ret_type, $fname, $args_spec) = $_ =~ /^(\w+)\s+([^\s]+)\s*\((.*)\)/;
	die("Could't parse: $_") if !$ret_type;
	# /* fname_short */
	my $fname_short = $fname;
	$fname_short =~ s/^fz_//;
	# /* args_short */
	my @args = map { 
		my @parts = split (/\s+/, $_);
		{ type => join (' ', @parts[0..($#parts-1)]),
		  name => $parts[$#parts]
		}
	} (split ',', $args_spec);
	foreach(@args) { $_->{asterik} = $_->{name} =~ s/^\*// ? '*' : '' }
	my $args_short = join ', ', map { $_->{name} } @args;
	push @fuse_init_spec, "fuse_ops.$fname_short = $fname;";

	push @fslayer_spec, "virtual $ret_type fuse_$fname_short($args_spec, PtrList<FSLayer*> *next = NULL);";
	push @fslayer_methods_spec, <<FUNCTION

$ret_type FSLayer::fuse_$fname_short($args_spec, PtrList<FSLayer*> *next) {
	if(next)
		return next->value->fuse_$fname_short($args_short);
	else
		return -1;
}
FUNCTION
;
	push @fslayer_macross, "#define FUSE_"
				.uc($fname_short)
				."($args_short)"
				." virtual $ret_type fuse_$fname_short("
				.join (', ', map { $_->{name} } @args)
				.", PtrList<FSLayer*> *next)";

	push (@fuse_private_methods_spec, <<PROTOTYPE 
int $fname($args_spec) {
	return fuse_context->value->fuse_$fname_short($args_short, fuse_context);
}
PROTOTYPE
	);
}

open FSLAYER_H, ">fslayer.h";
print FSLAYER_H <<CHUNK
#ifndef __FSLAYER_H
#define __FSLAYER_H
/* Autogenerated by $0 */
#include <fuse.h>
#include "ptrlist.h"
class FSLayer;
#include "fscontroller.h"
#define FUSE_NEXT next->value
#define FUSE_NEXT_LAYER next->next
CHUNK
;
print FSLAYER_H "\n";
print FSLAYER_H join ("\n", @fslayer_macross);
print FSLAYER_H "\n";
print FSLAYER_H <<CHUNK
using namespace std;
class FSLayer {
	public:
CHUNK
;
print FSLAYER_H join ("\n", map { "\t$_"} @fslayer_spec);
print FSLAYER_H <<CHUNK

	protected:
	FSController *controller;
	friend class FSController;
};
CHUNK
;
print FSLAYER_H "\n#endif\n";
close FSLAYER_H;
open FSLAYER_CPP, ">fslayer.cpp";
print FSLAYER_CPP <<CHUNK 
/* Autogenerated by $0 */
#include "fslayer.h"
CHUNK
;
print FSLAYER_CPP join ("\n", map { "\t$_"} @fslayer_methods_spec);
close FSLAYER_CPP;

open FUSE_COMPAT_CPP, ">fuse_compats.cpp";
print FUSE_COMPAT_CPP <<CHUNK 
#include <fuse.h>
#include "ptrlist.h"
#include "fslayer.h"

/* Autogenerated by $0 */
namespace FUSECompat {
PtrList <FSLayer*> *fuse_context;
struct fuse_operations fuse_ops;

CHUNK
;
print FUSE_COMPAT_CPP join ("\n", @fuse_private_methods_spec);
print FUSE_COMPAT_CPP "\n";
print FUSE_COMPAT_CPP <<CHUNK 
struct fuse_operations *init(PtrList <FSLayer*> *l) {
CHUNK
;
print FUSE_COMPAT_CPP join ("\n", map { "\t$_"} @fuse_init_spec);
print FUSE_COMPAT_CPP <<CHUNK 

	fuse_context = l;
	return(&fuse_ops);
}
CHUNK
;
print FUSE_COMPAT_CPP "};\n";
close FUSE_COMPAT_CPP;
